#pragma once

#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<mutex>
#include"UserManager.hpp"
#include"MsgQueue.hpp"
#include"ChatMsg.hpp"

#define THREAD_COUNT 4



class ChatServer
{
    public:
        ChatServer()
        {
            tcp_sock_ = -1;
            tcp_port_ = TCP_PORT;
            user_mana_ = NULL;
            epoll_fd_ = -1;
            thread_count_ = THREAD_COUNT;
            send_que_ = NULL;
            ready_sockfd_que_ = NULL;
            recv_que_ = NULL;
            status_que_ = NULL;
        }
        ~ChatServer()
        {}

        /*
         * 初始化
         *   tcp初始化
         *   epoll初始化
         *   用户管理模块初始化
         * */
        int ChatServerInit(uint16_t tcp_port = TCP_PORT, int thread_count = THREAD_COUNT)
        {
            tcp_port_ = tcp_port;
            thread_count_ = thread_count;
            //初始化TCP
            tcp_sock_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if(tcp_sock_ < 0)
            {
                perror("socket");
                return -1;
            }

            //端口重用
            int opt = 1;
            setsockopt(tcp_sock_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(tcp_port_);
            addr.sin_addr.s_addr = inet_addr("0.0.0.0");
            int ret = bind(tcp_sock_, (struct sockaddr*)&addr, sizeof(addr));
            if(ret < 0)
            {
                perror("bind");
                return -1;
            }
            ret = listen(tcp_sock_, 1024);
            if(ret < 0)
            {
                perror("listen");
                return -1;
            }

            //初始化epoll
            epoll_fd_ = epoll_create(8);
            if(epoll_fd_ < 0)
            {
                perror("epoll_create");
                return -1;
            }

            //初始化用户管理模块
            user_mana_ = new UserManager();
            if(NULL == user_mana_)
            {
                return -1;
            }
            if(false == user_mana_->UserManagerInit())
            {
                return -1;
            }
            //初始化各类队列
            send_que_ = new MsgQueue<ChatMsg>();
            if(NULL == send_que_)
            {
                return -1;
            }
            ready_sockfd_que_ = new MsgQueue<int>();
            if(NULL == ready_sockfd_que_)
            {
                return -1;
            }
            recv_que_ = new MsgQueue<ChatMsg>();
            if(NULL == recv_que_)
            {
                return -1;
            }
            status_que_ = new MsgQueue<std::pair<int, int>>();
            if(NULL == status_que_)
            {
                return -1;
            }
            return 0;
        }
        
        /* 
         * 启动各类线程服务的函数---主线程调用
         *    epoll等待线程、接收线程、发送线程、工作线程
         *    创建完毕后，该函数不退出，一直循环调用accept接收新连接
         * */
        int ChatServerStart()
        {
            //创建epoll等待线程
            pthread_t tid;
            int ret = pthread_create(&tid, NULL, epoll_wait_start, (void*)this);
            if(ret < 0)
            {
                perror("pthread_create");
                return -1;
            }
            //创建发送线程
            ret = pthread_create(&tid, NULL, send_msg_start, (void*)this);
            if(ret < 0)
            {
                perror("pthread_create");
                return -1;
            }
            //创建监听暂存队列的线程--轮询监控暂存队列，有可以发送的消息，将其push到发送队列
            ret = pthread_create(&tid, NULL, monitor_msg_start, (void*)this);
            if(ret < 0)
            {
                perror("pthread_create");
                return -1;
            }
            //创建工作线程
            for(int i = 0; i < thread_count_; i++)
            {
                ret = pthread_create(&tid, NULL, deal_start, (void*)this);
                if(ret < 0)
                {
                    thread_count_--;
                }
            }
            if(thread_count_ <= 0)
            {
                return -1;
            }
            //循环accept
            struct sockaddr_in cli_addr;
            socklen_t cli_addr_len = sizeof(cli_addr);
            while(1)
            {
                int new_sockfd = accept(tcp_sock_, (struct sockaddr*)&cli_addr, &cli_addr_len);
                if(new_sockfd < 0)
                {
                    continue;
                }
                //成功接收到一个新套接字描述符，添加到epoll中进行监控
                struct epoll_event ee;
                ee.events = EPOLLIN;
                ee.data.fd = new_sockfd;
                epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, new_sockfd, &ee);

            }

            return 0;
        }
        /*
         * 不同功能的线程入口函数
         * */
        static void* epoll_wait_start(void* arg)
        {
            pthread_detach(pthread_self());
            ChatServer* cs = (ChatServer*) arg;
            while(1)
            {
                struct epoll_event ee_arr[10];
                int ret = epoll_wait(cs->epoll_fd_, ee_arr, sizeof(ee_arr)/sizeof(ee_arr[0]), -1);
                if(ret < 0)
                {
                    continue;
                }

                /*
                 * 此时表示epoll监控到了就绪的事件结构，并且一定是新连接套接字对应的事件结构
                 *    将就绪的事件结构的文件描述符放入就绪队列中
                 * */
                for(int i = 0; i < ret; i++)
                {
                    cs->ready_sockfd_que_->Push(ee_arr[i].data.fd);
                }
                
                for(int i = 0; i < ret; i++)
                {
                    char buf[TCP_DATA_MAX_LEN] = {0};
                    
                    int sockfd = -1;
                    cs->ready_sockfd_que_->Pop(&sockfd);
                    //????存在tcp粘包问题
                    ssize_t recv_size = recv(sockfd, buf, sizeof(buf) - 1, 0);
                    if(recv_size < 0)
                    {
                        //接收失败
                        continue;
                    }
                    else if(recv_size == 0)
                    {
                        //对端将连接关闭了
                        epoll_ctl(cs->epoll_fd_, EPOLL_CTL_DEL, sockfd, NULL);
                        close(sockfd);
                        //组织一个更改用户状态的消息
                        cs->user_mana_->SetUserOffLine(sockfd);
                        continue;
                    }
                    /*
                     *  组织数据将其保存在接收数据的队列中，供工作线程使用
                     *
                     * */ 
                    
                    //正常接收
                    printf("epoll_wait_start recv msg : [%s] from tcp_sock [%d]\n", buf, sockfd);
                    //将读取到的数据反序列化
                    string msg;
                    msg.assign(buf, strlen(buf));

                    ChatMsg cm;
                    cm.PraseMsg(sockfd, msg);

                    //将反序列化后的数据push到接收队列中
                    cs->recv_que_->Push(cm);
                }

            }
            return NULL;
        }


        static void* send_msg_start(void* arg)
        {
            pthread_detach(pthread_self());
            ChatServer* cs = (ChatServer*) arg;
            while(1)
            {
                //从发送队列中获取数据
                ChatMsg cm;
                cs->send_que_->Pop(&cm);
                //将获取到的数据序列化
                string msg;
                cm.GetMsg(&msg);
                printf("send_msg_start: %s\n", msg.c_str());
                cout << "服务端发给" << cm.sockfd_ << endl;
                int send_size = send(cm.sockfd_, msg.c_str(), msg.size(), 0);
                if(send_size < 0)
                {
                    continue;
                }
            }
            return NULL;
        }
        //监控暂存队列的线程入口函数
        static void* monitor_msg_start(void* arg)
        {
            pthread_detach(pthread_self());
            ChatServer* cs = (ChatServer*)arg;

            while(1)
            {
                std::pair<int, int> usi;
                cs->status_que_->Pop(&usi);
                
                if(!cs->temp_msg_map_[usi.first].IsEmpty())
                {
                    sleep(1);
                    cs->temp_lock_.lock();
                    while(!cs->temp_msg_map_[usi.first].IsEmpty())
                    {
                        ChatMsg cm;
                        cs->temp_msg_map_[usi.first].Pop(&cm);
                        cm.sockfd_ = usi.second;

                        cs->send_que_->Push(cm);
                    }
                    cs->temp_lock_.unlock();
                }
            }
            return NULL;
        }



        //共工作线程
        static void* deal_start(void* arg)
        {
            pthread_detach(pthread_self());
            ChatServer* cs = (ChatServer*)arg;
            //通过业务类型分消息处理
            while(1)
            {
                ChatMsg cm;
                cs->recv_que_->Pop(&cm);
                int msg_type = cm.msg_type_;
                switch(msg_type)
                {
                    case Register:
                        cs->DealRegister(cm);
                        break;
                    case Login:
                        cs->DealLogin(cm);
                        break;
                    case AddFriend:
                        cs->DealAddFriend(cm);
                        break;
                    case PushAddFriendMsg_Resp:
                        cs->DealAddFriendResp(cm);
                        break;
                    case SendMsg:
                        cs->DealSendMsg(cm);
                        break;
                    case GetFriendMsg:
                        cs->GetAllFriendMsg(cm);
                        break;
                    default:
                        break;
                }
            }
        }

        void DealRegister(ChatMsg& cm)
        {
            if(cm.msg_type_ != Register)
            {
                return;
            }
            //1、获取注册信息
            string nickname = cm.GetValue("nickname");
            string telnum = cm.GetValue("telnum");
            string school = cm.GetValue("school");
            string passwd = cm.GetValue("passwd");
            //2、调用用户管理模块的注册接口
            int userid = -1;
            int ret = user_mana_->DealRegister(nickname, school, telnum, passwd, &userid);
            //3、组织应答数据
            cm.Clear();//清除请求时的信息
            cm.msg_type_ = Register_Resp;
            if(ret < 0)
            {
                //用户管理模块的注册失败了
                cm.reply_status_ = REGISTER_FAILED;
            }
            else
            {
                cm.reply_status_ = REGISTER_SUCCESS;
            }
            cm.user_id_ = userid;

            //将组织好的应答信息放回发送队列中
            send_que_->Push(cm);
        }
        void DealLogin(ChatMsg& cm)
        {
            //1、获取数据
            string telnum = cm.GetValue("telnum");
            string passwd = cm.GetValue("passwd");
            //2、调用用户管理模块的处理登录函数
            int ret = user_mana_->DealLogin(telnum, passwd, cm.sockfd_);
            //3、组织应答数据进行回复
            cm.Clear();
            cm.msg_type_ = Login_Resp;
            if(ret < 0)
            {
                cm.reply_status_ = LOGIN_FAILED;
            }
            else
            {
                cm.reply_status_ = LOGIN_SUCCESS;
            }

            cm.user_id_ = ret;
            //4、将组织好的数据存放到send_que_中
            send_que_->Push(cm);
            //5、将<userid, sockfd>键值对push到status_que_中
            status_que_->Push(std::make_pair(cm.user_id_, cm.sockfd_));
        }
        
        void DealSendMsg(ChatMsg& cm)
        {
            int send_id = cm.user_id_;//发送方的id
            int recv_id = cm.json_msg_["recvmsgid"].asInt();
            string send_msg = cm.json_msg_["msg"].asString();
            cm.Clear();
            //判断能否向接收方转发该消息
            UserInfo recv_ui;
            bool ret = user_mana_->GetUserInfo(recv_id, &recv_ui);
            //if(false == ret || recv_ui.user_status_ == OFFLINE)
            //{
            //    //接收方不存在或者不在线，服务端直接向发送发回复发送失败的应答
            //    cm.msg_type_ = SendMsg_Resp;
            //    cm.reply_status_ = SENDMSG_FAILED;
            //    send_que_->Push(cm);
            //    return;
            //}
            if(false == ret)
            {
                //接收方不存在,服务端直接向发送发回复发送失败的应答
                cm.msg_type_ = SendMsg_Resp;
                cm.reply_status_ = SENDMSG_FAILED;
                send_que_->Push(cm);
                return;
            }
            else if(recv_ui.user_status_ == OFFLINE)
            {
                //对方不在线
                cm.msg_type_ = SendMsg_Resp;
                cm.reply_status_ = SENDMSG_SUCCESS;
                send_que_->Push(cm);
                
                //组织给接收方的消息，放到暂存map中择机发送
                UserInfo send_ui;
                user_mana_->GetUserInfo(send_id, &send_ui);

                cm.Clear();
                cm.msg_type_ = PushMsg;
                cm.user_id_ = recv_id;
                cm.SetValue("peer_nickname", send_ui.nickname_);
                cm.SetValue("peer_school", send_ui.school_);
                cm.SetValue("peer_userid", send_ui.userid_);
                cm.SetValue("peer_msg", send_msg);

                temp_lock_.lock();
                temp_msg_map_[cm.user_id_].Push(cm);
                temp_lock_.unlock();

                return;
            }

            //说明对方存在并且在线，可以发送消息
            //先给发送方回复一条发送成功的应答消息
            cm.Clear();
            cm.msg_type_ = SendMsg_Resp;
            cm.reply_status_ = SENDMSG_SUCCESS;
            send_que_->Push(cm);

            //再组织数据给接收方发送消息
            UserInfo send_ui;
            user_mana_->GetUserInfo(send_id, &send_ui);

            cm.Clear();
            cm.msg_type_ = PushMsg;
            cm.sockfd_ = recv_ui.tcp_sock_;
            cm.SetValue("peer_nickname", send_ui.nickname_);
            cm.SetValue("peer_school", send_ui.school_);
            cm.SetValue("peer_userid", send_ui.userid_);
            cm.SetValue("peer_msg", send_msg);

            send_que_->Push(cm);
        }

        void GetAllFriendMsg(ChatMsg& cm)
        {
            //1、从客户端发来的消息中解析到userid,并调用用户管理模块的GetFriends接口获取所有好友的userid
            int userid = cm.user_id_;
            cm.Clear();
            vector<int> fri;
            if(user_mana_->GetFriends(userid, &fri) == false)
            {
                cm.reply_status_ = GETFRIEND_FAILED;
            }
            else
            {
                cm.reply_status_ = GETFRIEND_SUCCESS;
            }

            cm.msg_type_ = GetFriendMsg_Resp;
            //2、遍历fri中的userid,调用用户管理模块的getUserinfo接口获取每一个好友的其他信息
            for(size_t i = 0; i < fri.size(); i++)
            {
                UserInfo tmp;
                user_mana_->GetUserInfo(fri[i], &tmp);
                Json::Value j_v;
                j_v["nickname"] = tmp.nickname_;
                j_v["userid"] = tmp.userid_;
                j_v["school"] = tmp.school_;
                //对于每一个好友的信息，全部存储到应答信息中的json_msg_字段中
                cm.json_msg_.append(j_v);
            }
            send_que_->Push(cm);
        }

        void DealAddFriend(ChatMsg& cm)
        {
            //1、获取被添加方的电话号码
            string telnum = cm.GetValue("telnum");
            //2、查询被添加方是否处于登陆状态
            UserInfo be_add_ui;
            int ret = user_mana_->IsLogin(telnum, &be_add_ui);

            //获取添加方的用户信息
            UserInfo add_ui;
            if(user_mana_->GetUserInfo(cm.user_id_, &add_ui) == false)
            {
                cout << "get add_ui failed" << endl;
                return;
            }
            
            //3、给被添加方推送添加好友的信息
            cm.Clear();
            cm.msg_type_ = PushAddFriendMsg;
            if(ret < 0)
            {
                //被添加方不存在，服务端直接组织添加好友的应答消息给添加方
                cm.msg_type_ = AddFriend_Resp;
                cm.reply_status_ = ADDFRIEND_FAILED;
                cm.SetValue("error", "user not exits, please check friend telnum");

                send_que_->Push(cm);
                return;
            }
            else if(ret == OFFLINE)
            {
                //暂存消息，等到被添加方上线后将消息存储到发送队列，由发送线程转发
                cm.msg_type_ = PushAddFriendMsg;
                cm.user_id_ = be_add_ui.userid_;

                cm.SetValue("adder_userid", add_ui.userid_);
                cm.SetValue("adder_nickname", add_ui.nickname_);
                cm.SetValue("adder_school", add_ui.school_);

                temp_lock_.lock();
                temp_msg_map_[cm.user_id_].Push(cm);
                temp_lock_.unlock();

                return;
            }
            //ONLINE状态
            //被添加方存在并且处于在线状态，组织推送添加好友信息的消息格式，并发送给被添加方
            cm.msg_type_ = PushAddFriendMsg;

            cm.sockfd_ = be_add_ui.tcp_sock_;
            cm.SetValue("adder_userid", add_ui.userid_);
            cm.SetValue("adder_nickname", add_ui.nickname_);
            cm.SetValue("adder_school", add_ui.school_);

            send_que_->Push(cm);
        }

        void DealAddFriendResp(ChatMsg& cm)
        {
            //1、获取双方用户的信息
            int reply_status = cm.reply_status_;
            //被添加方的用户信息
            UserInfo be_add_user;
            user_mana_->GetUserInfo(cm.user_id_, &be_add_user);
            //获取添加方的用户信息
            UserInfo add_ui;
            int add_userid = atoi(cm.GetValue("userid").c_str());
            user_mana_->GetUserInfo(add_userid, &add_ui);
            //2、判断响应状态
            cm.Clear();
            cm.msg_type_ = AddFriend_Resp;

            if(reply_status == ADDFRIEND_FAILED)
            {
                cm.reply_status_ = ADDFRIEND_FAILED;
                string content = "add user " + be_add_user.nickname_ + " failed!";
                cm.SetValue("content", content);
            }
            else if(reply_status == ADDFRIEND_SUCCESS)
            {
                cm.reply_status_ = ADDFRIEND_SUCCESS;
                string content = "add user " + be_add_user.nickname_ + " success!";
                cm.SetValue("content", content);
                cm.SetValue("peer_nickname", be_add_user.nickname_);
                cm.SetValue("peer_school", be_add_user.school_);
                cm.SetValue("peer_userid", be_add_user.userid_);

                //用户管理模块维护好友信息并更新数据库表
                user_mana_->SetFriend(add_userid, be_add_user.userid_);
            }
            cm.sockfd_ = add_ui.tcp_sock_;


            if(add_ui.user_status_ == OFFLINE)
            {
                //消息暂存，择机发送
            }
            //3、给添加方推送应答响应
            std::cout << "这是服务端向客户端推送的添加好友应答的消息" << endl;
            send_que_->Push(cm);
        }

    private:
        //侦听套接字&端口
        int tcp_sock_;
        int tcp_port_;
        //用户管理模块的实例化指针
        UserManager* user_mana_;
        //epoll操作句柄
        int epoll_fd_;
        //工作线程的数量
        int thread_count_;

        //发送线程的队列
        MsgQueue<ChatMsg>* send_que_;
        //保存epoll_wait监听到的就绪的文件描述符
        MsgQueue<int>* ready_sockfd_que_;
        //接收消息的队列
        MsgQueue<ChatMsg>* recv_que_;
        //监控队列&&暂存消息的map
        MsgQueue<std::pair<int, int>>* status_que_;//<userid, sockfd>
        std::map<int, MsgQueue<ChatMsg>> temp_msg_map_;//<userid, queue<ChatMsg>>
        std::mutex temp_lock_;
};
